#include <types.h>
#include <Proc.h>
#include <errno.h>

bool Scheduler::Initialized;
Spinlock Scheduler::Lock;
List Scheduler::ReadyList;
Process *Scheduler::Current;
Process *Scheduler::Next;

static volatile dword ctxsw = 0;
static int tmstick = 10;

void Scheduler::Yield() {
	volatile dword oldctxsw = ctxsw;
	Scheduler::Current->ticks = 0;
	while (oldctxsw == ctxsw)
		CPU::Halt();
}

void Scheduler::WakeUp(Process *p) {
	((List *) p)->Remove();
	p->state = psReady;
	ReadyList.Add((List *) p);
}

void Scheduler::SleepOn(List * wait) {
	((List *) Current)->Remove();
	Current->state = psSleeping;
	if (wait) {
		wait->Add((List *) Current);
	}
}

int Scheduler::Sleep(dword ms) {
	dword ticks;
	Lock.Lock();
	ticks = ms / 10;
	Current->counter = ticks;
	SleepOn(NULL);
	Lock.Unlock();
	Yield();
	if (!Current->counter) {
		return -EINTR;
	}
	return 0;
}

void Scheduler::Schedule() {
	int i, Processes;

	ctxsw++;

	if (Current->state == psRunning) {
		Current->tms.tms_stime += tmstick;
		Current->state = psReady;
	}

	Processes = Proc::Stat(NULL);
	i = 0;
	while (Processes) {
		if (!Proc::proctab[i]) {
			i++;
			continue;
		}
		if ((Proc::proctab[i]->state == psSleeping) && (Proc::proctab[i]->counter > 0)) {
			Proc::proctab[i]->counter--;
			if (!Proc::proctab[i]->counter)
				WakeUp(Proc::proctab[i]);
		}

		Processes--;
		i++;
	}

	if (Current->state == psReady) {
		Current->ticks--;
		if (Current->ticks > 0) {
			Current->state = psRunning;
			return;
		}
	}

	if (ReadyList.isEmpty());
//		panic("Empty ready list (no process to run)");

	Current = Next;

	// Something is wrong, start executing from first task on list
	if (Current->state != psReady) {
		Current = (Process *) ReadyList.next;
		Next = Current;
	}

	Next = (Process *) Next->list.next;

	Current->ticks = Current->priority;
	Current->state = psRunning;
}
